package com.bflow.impl;

import com.bflow.api.*;
import com.bflow.api.bo.*;
import com.bflow.api.constant.BflowConstant;
import com.bflow.api.constant.BflowConstant.ActAssignTypes;
import com.bflow.api.constant.BflowConstant.CommStatus;
import com.bflow.api.constant.BflowConstant.RunTaskStatus;
import com.bflow.commons.bean.PageBean;
import com.bflow.commons.bean.PageResult;
import com.bflow.commons.consts.CommConst;
import com.bflow.commons.exception.BizException;
import com.bflow.commons.service.BaseService;
import com.bflow.commons.util.StrUtil;
import com.bflow.dao.BflowRunTaskDao;
import com.bflow.dao.po.BflowRunTaskPo;
import com.bflow.util.*;
import javafx.beans.binding.MapBinding;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static com.bflow.commons.consts.CommConst.STATUS_Y;

/**
 * 功能：BflowRunTask 任务-实现业务接口
 * 作者：haowen
 * 日期：2018-12-13
 */
@Service("bflowRunTaskService")
public class BflowRunTaskServiceImpl extends BaseService<BflowRunTaskPo, BflowRunTaskBo> implements IBflowRunTaskService<BflowRunTaskPo, BflowRunTaskBo> {


	@Autowired
	private BflowRunTaskDao dao;

	@Autowired
	private IBflowRunInstanceService<?, BflowRunInstanceBo> bflowRunInstanceService;
	@Autowired
	private IBflowDefineActService<?, BflowDefineActBo> bflowDefineActService;
	@Autowired
	private IBflowDefineActRouteService<?, BflowDefineActRouteBo> bflowDefineActRouteService;
	@Autowired
	private IBflowDefineProcessService<?, BflowDefineProcessBo> bflowDefineProcessService;
	@Autowired
	private IBflowRunRecordService<?, BflowRunRecordBo> bflowRunRecordService;
	@Autowired
	private IBflowExtResService bflowExtResService;

	@Override
	protected BflowRunTaskDao getDao() {
		return dao;
	}

	@Override
	public void add(BflowRunTaskBo bo) {
		bo.setId(SysSeqUtil.genId());
		bo.setCreateTime(new Date());
		BflowRunTaskPo po = BeanMapper.map(bo, BflowRunTaskPo.class);
		if (dao.insert(po) <= 0) {
			throw new BizException("新增失败");
		}
	}


	@Transactional
	@Override
	public void addNextTask(String nextActId, String assignee, String instanceId, String procId, BflowRunTaskAuditBo auditBo){
		AssertUtil.isNotBlank(nextActId, "无下个活动, 流程无法进行！");
		AssertUtil.isNotBlank(assignee, "下个活动的参与者标识不存在,流程无法进行！");
		BflowRunTaskBo taskBo = new BflowRunTaskBo();
		taskBo.setProcId(procId);
		taskBo.setInstanceId(instanceId);
		taskBo.setActId(nextActId);
		taskBo.setUserKey(assignee);
		taskBo.setStatus(RunTaskStatus.WAITING_DEAL.getCode());
		taskBo.setCreateUid(auditBo.getOperationUserId());
		taskBo.setCreateUname(auditBo.getOperationUserName());
		taskBo.setDelFlag(STATUS_Y);
		//插入任务数据
		logger.info("保存的任务数据：" + JsonUtil.toStr(taskBo));
		add(taskBo);
	}

	private void addNextTask(String nextActId, BflowRunTaskBo currTaskBo, BflowRunTaskAuditBo auditBo){
		BflowDefineActBo nextAct = bflowDefineActService.getUsableActById(nextActId);
		AssertUtil.isNotNull(nextAct, "无下个活动, 流程无法进行！");
		BflowDefineProcessBo processBo = bflowDefineProcessService.findById(currTaskBo.getProcId());
		//传入的下个处理人
		String assignee = auditBo.getVal(BflowConstant.PARAM_NAME_NEXT_ASSIGNEE);
		//获取下个处理人
		assignee = getNextActAssignee(currTaskBo.getActSort(), assignee, nextAct, processBo);
//		nextActId = nextActId+"_"+nextActSort;
		addNextTask(nextActId, assignee, currTaskBo.getInstanceId(), currTaskBo.getProcId(), auditBo);
	}


	@Override
	public void cancelTask(String instanceId) throws BizException {
		AssertUtil.isNotNull(instanceId, "instanceId不能为空");
		BflowRunTaskBo b=new BflowRunTaskBo();
		b.setInstanceId(instanceId);
		List<BflowRunTaskBo> tasks = this.getListBy(b);
		if(CollectionUtils.isNotEmpty(tasks)){
			//禁用
			int flag=CommStatus.N.getIntCode();
			this.getDao().cancelTask(instanceId,flag,new Date());
		}
	}

	@Override
	public void assign(BflowRunTaskAuditBo auditBo, ActAssignTypes assignType, String receiveByKey) {
		AssertUtil.isNotBlank(auditBo.getTaskId(), "任务ID 不能为空");
		AssertUtil.isNotBlank(auditBo.getOperationUserId(), "操作人ID 不能为空");
		AssertUtil.isNotBlank(receiveByKey, "下个处理人 不能为空");

		if(StrUtil.isBlank(auditBo.getRemarks()))
			auditBo.setRemarks("分派完毕");
		//传入的下个处理人
		auditBo.addVals(BflowConstant.PARAM_NAME_NEXT_ASSIGNEE, receiveByKey);
		BflowRunTaskBo taskBo = findById(auditBo.getTaskId());
		AssertUtil.isNotNull(taskBo, "任务数据不存在,流程无法进行！");
		if(StrUtil.isBlank(auditBo.getRouteId())){
			//查找 下个路由
			BflowDefineActRouteBo routeBo = ActRouteUtil.findNextActRoute(taskBo.getProcId(), taskBo.getActIdVal(), assignType);
			AssertUtil.isNotNull(routeBo, "找不到下个路由, 分派失败！");
			//设置 下个路由
			auditBo.setRouteId(routeBo.getId());
		}
		processTask(auditBo);
	}


	@Transactional
	@Override
	public void process(BflowRunTaskAuditBo auditBo) {
		//验证参数
		this.validAuditParam(auditBo);
		processTask(auditBo);
	}

	private void processTask(BflowRunTaskAuditBo auditBo) {
		String taskId = auditBo.getTaskId();
		BflowRunTaskBo taskBo = findById(taskId);
		AssertUtil.isNotNull(taskBo, "任务数据不存在,流程无法进行！");
		BflowDefineActRouteBo actRouteBo = bflowDefineActRouteService.getRouteItemById(auditBo.getRouteId());
		AssertUtil.isNotNull(actRouteBo, "活动路由数据不存在,流程无法进行！");
		auditBo.setRouteText(actRouteBo.getCondDesc()); //设置条件描述
		//完成当前任务
		finishCurrentTask(taskBo, auditBo);

		String instanceId = taskBo.getInstanceId();
		String nextActId = actRouteBo.getNextActId();
		if (StrUtil.isNotBlank(nextActId)) {
			logger.info("进入下一步 instanceId:" + instanceId);
//			Integer actSort = ActRouteUtil.getActSortByActId(routeBos, nextActId);
			addNextTask(nextActId, taskBo, auditBo);
		} else {
			logger.info("无下一步 结束流程 instanceId:" + instanceId);
			bflowRunInstanceService.stopProcess(instanceId);
		}
	}


	/**
	 * 获取 任务信息
	 * @param taskId
	 * @param userId
	 * @param getRecords 获取记录
	 * @param getActRoutes 获取路由
	 * @return
	 */
	@Override
	public BflowRunTaskBo getTaskInfo(String taskId, String userId, boolean getRecords, boolean getActRoutes) {
		BflowRunTaskBo taskBo = findById(taskId);
		AssertUtil.isNotNull(taskBo, taskId + " 任务不存在");
		//检查 处理人信息身份
		checkAssigneeInfo(userId, taskBo);
		taskBo.setInstance(bflowRunInstanceService.get(taskBo.getInstanceId(), userId, getRecords));
		if(getActRoutes){
			//设置 路由集合
			taskBo.setActRoutes(bflowDefineActRouteService.findListByProcId(taskBo.getActIdVal(), taskBo.getProcId(), CommStatus.Y));
		}
		return taskBo;
	}

	//检查 处理人信息身份
	private void checkAssigneeInfo(String userId, BflowRunTaskBo taskBo){
		BflowDefineActBo actBo = bflowDefineActService.getCacheById(taskBo.getActIdVal());
		String assigneeType = actBo.getAssigneeType();
		if(ActAssignTypes.ASSIGN_ROLE_KEY.eq(assigneeType) || ActAssignTypes.ROLE_KEY.eq(assigneeType)){
			AssertUtil.isTrue(bflowExtResService.hasRoleKey(userId, taskBo.getUserKey()), "对不起, 您没有操作权限");
		}else{
			AssertUtil.assertEqual(userId, taskBo.getUserKey(), "对不起, 您没有操作权限");
		}
	}

	private void finishCurrentTask(BflowRunTaskBo taskBo, BflowRunTaskAuditBo auditBo) {
		//结束当前任务
		BflowRunTaskBo finishTaskBo = new BflowRunTaskBo();
		finishTaskBo.setId(taskBo.getId());
		finishTaskBo.setStatus(RunTaskStatus.FINISH_DEAL.getCode());
		finishTaskBo.setFinishTime(new Date());
		update(finishTaskBo);

		//添加 记录
		BflowRunRecordBo bflowRunRecordBo = new BflowRunRecordBo(auditBo);
		bflowRunRecordBo.setInstanceId(taskBo.getInstanceId());
		bflowRunRecordBo.setTaskId(taskBo.getId());
		bflowRunRecordBo.setFlag(auditBo.getRouteText());
		bflowRunRecordBo.setVersion(taskBo.getVersion());
		bflowRunRecordBo.setCurActId(taskBo.getActId());
		//插入记录数据
		logger.info("保存的记录数据：" + JsonUtil.toStr(bflowRunRecordBo));
		bflowRunRecordService.add(bflowRunRecordBo);
	}


	/**
	 * 验证 审核参数
	 * @param auditBo
	 */
	protected void validAuditParam(BflowRunTaskAuditBo auditBo) {
		AssertUtil.isNotNull(auditBo, "提交的请假审批内容 不能为空");
		AssertUtil.isNotBlank(auditBo.getTaskId(), "任务ID 不能为空");
		AssertUtil.isNotBlank(auditBo.getOperationUserId(), "操作人ID 不能为空");
		AssertUtil.isNotBlank(auditBo.getRouteId(), "审批结论 不能为空");
		AssertUtil.isNotBlank(auditBo.getRemarks(), "审批意见 不能为空");
	}


	@Override
	public BflowWaitDisposeBo getWaitDisposeTaskList(String userId, String procKey,String actId,String userKey) {
		BflowWaitDisposeBo result=new BflowWaitDisposeBo();
		AssertUtil.isNotNull(userId, "用户id不能为空");
		//AssertUtil.isNotNull(procKey, "instanceKey不能为空");
		List<BflowRunTaskPo> waitDisposeTaskList = this.getDao().getWaitDisposeTaskList(userId, procKey, actId, userKey);
		List<BflowRunTaskBo> tasks = BeanMapper.mapList(waitDisposeTaskList,BflowRunTaskBo.class);
		if(CollectionUtils.isEmpty(tasks)){
			return null;
		}
		result.setTasks(tasks);
		String instanceId= tasks.get(0).getInstanceId();

		for(BflowRunTaskBo b:tasks){
			b.setInstance(bflowRunInstanceService.findById(instanceId));
			String procId = b.getProcId();
			if(StringUtils.isNotEmpty(b.getActId())){
				String[] s = b.getActId().split("_");
				actId=s[0];
				Map<String, BflowDefineActBo> actMap = this.getActMap(actId, procId);
				if(actMap!=null){
					BflowDefineActBo currAct = actMap.get("currAct");
					BflowDefineActBo nextAct = actMap.get("nextAct");
					BflowDefineActBo prevAct = actMap.get("prevAct");
					b.setAct(currAct);
					b.setNextAct(nextAct);
					b.setPrevAct(prevAct);
				}else{
					//为空则是请假审批

				}
				BflowDefineActBo act = bflowDefineActService.findById(actId);
				b.setAct(act);
			}
			BflowDefineProcessBo byId = bflowDefineProcessService.findById(procId);
			b.setProcess(byId);
		}
		//result.setInstanceBo(instanceBo);
		return result;
	}

	/**
	 * 根据活动id+流程id 获取上一个 下一个活动
	 * @param actId
	 * @return
	 */
	private Map<String,BflowDefineActBo> getActMap(String actId,String procId){
		Map<String,BflowDefineActBo> result = new HashMap<>();
		result.put("currAct",null);
		result.put("nextAct",null);
		result.put("prevAct",null);
		BflowDefineActRouteBo routeQuery=new BflowDefineActRouteBo();
		routeQuery.setActId(actId);
		routeQuery.setProcId(procId);
		BflowDefineActRouteBo currRoute = bflowDefineActRouteService.getOneBy(routeQuery);
		//此处为空 则是请假审批
		if(currRoute==null){
			return null;
		}
		BflowDefineActBo currAct = bflowDefineActService.findById(currRoute.getActId());
		result.put("currAct",currAct);
		//此处为空 则是请假审批
		if(currAct==null){
			return null;
		}
//		String nextActId = currRoute.getNextActId();
//		BflowDefineActBo nextAct = bflowDefineActService.findById(nextActId);
//		result.put("nextAct",nextAct);
		routeQuery.setNextActId(currRoute.getActId());
		BflowDefineActRouteBo prevRoute = bflowDefineActRouteService.getOneBy(routeQuery);
		if(prevRoute!=null){
			BflowDefineActBo prevAct = bflowDefineActService.findById(prevRoute.getActId());
			result.put("prevAct",prevAct);
		}
		return result;
	}


	@Override
	public long countWaitDisposeTask(String userId, String procKey,String actId) {
		AssertUtil.isNotNull(userId,"用户id不能为空");
		//AssertUtil.isNotNull(procKey, "instanceKey不能为空");
		long count = this.getDao().countWaitDisposeTask(userId,procKey,actId);
		return count;
	}

	@Override
	public PageResult<BflowRunTaskBo> findMyTaskPage(PageBean pageBean, String busiKey, String actKey, String userKey) throws BizException {
		BflowRunTaskQueryParam queryParam = new BflowRunTaskQueryParam();
		BflowRunTaskBo queryBo = new BflowRunTaskBo();
		queryBo.setDelFlag(CommConst.STATUS_Y);
		queryBo.setStatus(RunTaskStatus.WAITING_DEAL.getCode());
		queryBo.setUserKey(userKey);
		if(StrUtil.isNotBlank(actKey)){
			BflowDefineActBo actBo = bflowDefineActService.getCacheByKey(actKey);
			AssertUtil.isNotNull(actBo, "查询失败，参数:"+actKey+" 不存在，请联系管理员");
			queryBo.setActId(actBo.getId());
		}
		BflowRunInstanceBo queryInstance = new BflowRunInstanceBo();
		queryInstance.setBusiKey(busiKey);
		queryBo.setInstance(queryInstance);
		queryParam.setBflowRunTask(queryBo);
		queryParam.setDefParam(1, 10, "task.CREATE_TIME", "DESC");
		PageResult<BflowRunTaskBo> pageResult = findTaskInstancePage(queryParam);
		return pageResult;
	}


	/**
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
	 * 查询任务列表以及关联业务数据
	 * @param queryParam 查询条件
	 * @return
	 */
	@Override
	public PageResult<BflowRunTaskBo> findTaskInstancePage(BflowRunTaskQueryParam queryParam) throws BizException {
		queryParam.setPageDefParam(1, 10); //防止分页为空 设置默认值
		PageResult<BflowRunTaskBo> pageResult = new PageResult<BflowRunTaskBo>();
		List<BflowRunTaskBo> dataList = null;
		long totalCount = getDao().findTaskInstancePageCount(queryParam);
		if(queryParam.getPageBean().getStart() < totalCount){
			List<BflowRunTaskPo> poList = getDao().findTaskInstancePage(queryParam);
			if (CollectionUtils.isNotEmpty(poList)) {
				dataList = BeanMapper.mapList(poList, getBoClazz());
			}
		}
		pageResult.setTotalCount(totalCount);
		pageResult.setResult(dataList);
		return pageResult;
	}

	/**
	 * 获取 下个活动 处理人
	 * @param actSort
	 * @param paramAssignee 外部参数
	 * @param defineActBo
	 * @param processBo
     * @return
     */
	@Override
	public String getNextActAssignee(Integer actSort, String paramAssignee, BflowDefineActBo defineActBo, BflowDefineProcessBo processBo) {
		String userKey = null;
		String[] defAssigneeArr = processBo.getActAssigneeArray();
		if(ArrayUtils.isNotEmpty(defAssigneeArr)){
			userKey = getDefineProcessAssignee(processBo.getActAssigneeArray(), actSort);
		}else{
			//分派类型
			if(ActAssignTypes.ASSIGN_ROLE_KEY.eq(defineActBo.getAssigneeType())
					|| ActAssignTypes.ASSIGN_USER_ID.eq(defineActBo.getAssigneeType())){
				AssertUtil.isNotBlank(paramAssignee, "未指定 "+BflowConstant.PARAM_NAME_NEXT_ASSIGNEE +" 参数");
				checkExtResAssignee(paramAssignee, defineActBo.getAssigneeType());
					userKey = paramAssignee;
			}else {
				AssertUtil.isNotBlank(defineActBo.getAssignee(), "下个节点的默认配置有误");
				checkExtResAssignee(defineActBo.getAssignee(), defineActBo.getAssigneeType());
				userKey = defineActBo.getAssignee();
			}
		}
		return userKey;
	}


	private String getDefineProcessAssignee(String[] actAssigneeArray, int actSort){
		//设置 处理人
		AssertUtil.isTrue(actAssigneeArray.length>actSort, "流程配置有误");
		return actAssigneeArray[actSort];
	}

	//检查 处理人
	private void checkExtResAssignee(String assignee, String assigneeType){
		if(ActAssignTypes.ASSIGN_ROLE_KEY.eq(assigneeType) || ActAssignTypes.ROLE_KEY.eq(assigneeType)){
			AssertUtil.isNotBlank(bflowExtResService.getRoleName(assignee), "处理人参数 "+assignee+" 有误,无法找到对应角色");
		}else{
			AssertUtil.isNotBlank(bflowExtResService.getUserName(assignee), "处理人参数 "+assignee+" 有误,无法找到对应用户");
		}
	}

	/**
	 * 查询任务列表以及关联业务数据记录数
	 * @param queryParam 查询条件
	 * @return
	 */
	@Override
	public long findTaskInstancePageCount(BflowRunTaskQueryParam queryParam){
		return getDao().findTaskInstancePageCount(queryParam);
	}

	/**
	 * 获取待办任务数
	 * @param userKey 代办者标识,无则查询全部代办者的待办数
	 * @param busiKey 流程业务KEY,无则查询全部流程的待办数
	 * @return
	 */
	@Override
	public Long getWaitTaskCount(String userKey, String busiKey){
		BflowRunTaskQueryParam queryParam = new BflowRunTaskQueryParam();
		BflowRunTaskBo queryBo = new BflowRunTaskBo();
		queryBo.setDelFlag(CommConst.STATUS_Y);
		queryBo.setStatus(RunTaskStatus.WAITING_DEAL.getCode());
		queryBo.setUserKey(userKey);

		BflowRunInstanceBo queryInstance = new BflowRunInstanceBo();
		queryInstance.setBusiKey(busiKey);
		queryBo.setInstance(queryInstance);
		queryParam.setBflowRunTask(queryBo);
		return this.findTaskInstancePageCount(queryParam);
	}

	@Override
	public List<BflowRunTaskBo> findWaitTaskList(String instanceId) throws BizException {
		BflowRunTaskBo queryBo = new BflowRunTaskBo();
		queryBo.setInstanceId(instanceId);
		queryBo.setDelFlag(CommConst.STATUS_Y);
		queryBo.setStatus(RunTaskStatus.WAITING_DEAL.getCode());
		return getListBy(queryBo);
	}

	@Override
	@Transactional
	public void transfer(BflowRunTaskAuditBo paramBo, ActAssignTypes assignType, String transferUserId) {
		AssertUtil.isNotBlank(paramBo.getTaskId(), "任务ID 不能为空");
		AssertUtil.isNotBlank(paramBo.getOperationUserId(), "操作人ID 不能为空");
		AssertUtil.isNotBlank(transferUserId, "转交人 不能为空");
		BflowRunTaskBo taskBo = this.findById(paramBo.getTaskId());
		AssertUtil.isNotEmpty(taskBo, "无效的任务");
		//设置当前处理人已完成此任务
		this.finishCurrentTask(taskBo,paramBo);
		//新建转交人任务
		this.addNextTask(taskBo.getActId(),transferUserId,taskBo.getInstanceId(),taskBo.getProcId(),paramBo);
	}

}
